package com.paul.sys.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.Properties;

/**
 * Manages properties for the entire web application system. Properties are merely
 * pieces of information that need to be saved in between server restarts.
 * <p>
 * At the moment, properties are stored in a Java Properties file.
 * <p>
 * <p>
 * This class is implemented as a singleton since many classloaders seem to
 * take issue with doing classpath resource loading from a static context.
 */
public class PropertyManager {

    private static PropertyManager manager = null;
    private static Object managerLock = new Object();
    private static String propsName = "/setting.properties";

    private Properties properties = null;
    private Object propertiesLock = new Object();
    private String resourceURI;

	/**
	 * Creates a new PropertyManager. Singleton access only.
	 */
	private PropertyManager(String resourceURI) {
		this.resourceURI = resourceURI;
	}
	protected void deleteProp(String name) {
		//Only one thread should be writing to the file system at once.
		synchronized (propertiesLock) {
			//Create the properties object if necessary.
			if (properties == null) {
				loadProps();
			}
			properties.remove(name);
			saveProps();
		}
	}
	/**
	 * Deletes a Jive property. If the property doesn't exist, the method
	 * does nothing.
	 *
	 * @param name the name of the property to delete.
	 */
	public static void deleteProperty(String name) {
		if (manager == null) {
			synchronized(managerLock) {
				if (manager == null) {
					manager = new PropertyManager(propsName);
				}
			}
		}
		manager.deleteProp(name);
	}
/**
 * Gets a property. properties are stored in a properties file.
 * The properties file should be accesible from the classpath. Additionally,
 * it should have a path field that gives the full path to where the
 * file is located. Getting properties is a fast operation.
 *
 * @param name the name of the property to get.
 * @return the property specified by name.
 */
protected String getProp(String name) {
    //If properties aren't loaded yet. We also need to make this thread
    //safe, so synchronize...
    if (properties == null) {
        synchronized (propertiesLock) {
            //Need an additional check
            if (properties == null) {
                loadProps();
            }
        }
    }
    String property = properties.getProperty(name);
    if (property == null) {
        return null;
    } else {
        return property.trim();
    }
}
	/**
	 * Returns a Jive property.
	 *
	 * @param name the name of the property to return.
	 * @return the property value specified by name.
	 */
	public static String getProperty(String name) {
		if (manager == null) {
			synchronized(managerLock) {
				if (manager == null) {
					manager = new PropertyManager(propsName);
				}
			}
		}
		return manager.getProp(name);
	}
/**
	 * Loads properties file from the disk.
	 */
private void loadProps() {
    properties= new Properties();
    InputStream in= null;
    try {
        in= getClass().getResourceAsStream(resourceURI);
        properties.load(in);
    } catch (Exception e) {
        System.err.println("Error reading properties in PropertyManager.loadProps() " + e);
        e.printStackTrace();
    } finally {
        try {
            in.close();
        } catch (Exception e) {
            System.err.println("Error reading properties in PropertyManager.loadProps() " + e);
            e.printStackTrace();
        }
    }
}
	/**
	 * Returns true if the jive.properties file exists where the path property
	 * purports that it does.
	 */
	public static boolean propertyFileExists() {
		if (manager == null) {
			synchronized(managerLock) {
				if (manager == null) {
					manager = new PropertyManager(propsName);
				}
			}
		}
		return manager.propFileExists();
	}
	/**
	 * Returns true if the properties are readable. This method is mainly
	 * valuable at setup time to ensure that the properties file is setup
	 * correctly.
	 */
	public static boolean propertyFileIsReadable() {
		if (manager == null) {
			synchronized(managerLock) {
				if (manager == null) {
					manager = new PropertyManager(propsName);
				}
			}
		}
		return manager.propFileIsReadable();
	}
	/**
	 * Returns true if the properties are writable. This method is mainly
	 * valuable at setup time to ensure that the properties file is setup
	 * correctly.
	 */
	public static boolean propertyFileIsWritable() {
		if (manager == null) {
			synchronized(managerLock) {
				if (manager == null) {
					manager = new PropertyManager(propsName);
				}
			}
		}
		return manager.propFileIsWritable();
	}
/**
	 * Returns the names of the properties.
	 *
	 * @return an Enumeration of the property names.
	 */
public static Enumeration propertyNames() {
	if (manager == null) {
		synchronized (managerLock) {
			if (manager == null) {
				manager = new PropertyManager(propsName);
			}
		}
	}
	return manager.propNames();
}
	/**
	 * Returns true if the jive.properties file exists where the path property
	 * purports that it does.
	 */
	public boolean propFileExists() {
		String path = getProp("path");
		File file = new File(path);
		if (file.isFile()) {
			return true;
		}
		else {
			return false;
		}
	}
	/**
	 * Returns true if the properties are readable. This method is mainly
	 * valuable at setup time to ensure that the properties file is setup
	 * correctly.
	 */
	public boolean propFileIsReadable() {
		try {
			InputStream in = getClass().getResourceAsStream(resourceURI);
			return true;
		}
		catch (Exception e) {
			return false;
		}
	}
/**
 * Returns true if the properties are writable. This method is mainly
 * valuable at setup time to ensure that the properties file is setup
 * correctly.
 */
public boolean propFileIsWritable() {
    String path = getProp("path");
    File file = new File(path);
    if (file.isFile()) {
        //See if we can write to the file
        if (file.canWrite()) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}
	protected Enumeration propNames() {
		//If properties aren't loaded yet. We also need to make this thread
		//safe, so synchronize...
		if (properties == null) {
			synchronized(propertiesLock) {
				//Need an additional check
				if (properties == null) {
					loadProps();
				}
			}
		}
		return properties.propertyNames();
	}
/**
 * Saves properties to disk.
 */
private void saveProps() {
    //Now, save the properties to disk. In order for this to work, the user
    //needs to have set the path field in the properties file. Trim
    //the String to make sure there are no extra spaces.
    String path = properties.getProperty("path").trim();
    OutputStream out = null;
    try {
        out = new FileOutputStream(path);
        properties.store(out, "redbeans.properties -- " + (new java.util.Date()));
    } catch (Exception ioe) {
        System.err.println(
            "There was an error writing redbeans.properties to "
                + path
                + ". "
                + "Ensure that the path exists and that the Jive process has permission "
                + "to write to it -- "
                + ioe);
        ioe.printStackTrace();
    } finally {
        try {
            out.close();
        } catch (Exception e) {
        }
    }
}
/**
 * Sets a property. Because the properties must be saved to disk
 * every time a property is set, property setting is relatively slow.
 */
protected void setProp(String name, String value) {
    //Only one thread should be writing to the file system at once.
    synchronized (propertiesLock) {
        //Create the properties object if necessary.
        if (properties == null) {
            loadProps();
        }
        properties.setProperty(name, value);
        saveProps();
    }
}
	/**
	 * Sets a Jive property. If the property doesn't already eist, a new
	 * one will be created.
	 *
	 * @param name the name of the property being set.
	 * @param value the value of the property being set.
	 */
	public static void setProperty(String name, String value) {
		if (manager == null) {
			synchronized(managerLock) {
				if (manager == null) {
					manager = new PropertyManager(propsName);
				}
			}
		}
		manager.setProp(name, value);
	}
}
